package cm.tl.pms.sys.controller;

import cm.tl.pms.common.entity.ErrorEnum;
import cm.tl.pms.common.entity.ResultMsg;
import cm.tl.pms.common.exception.CustomErrorException;
import cm.tl.pms.sys.entity.SysUser;
import cm.tl.pms.sys.entity.SysUserRole;
import cm.tl.pms.sys.pojo.*;
import cm.tl.pms.sys.satoken.EasySaTokenListener;
import cm.tl.pms.sys.satoken.OnlineUser;
import cm.tl.pms.sys.service.ISysRoleService;
import cm.tl.pms.sys.service.ISysUserRoleService;
import cm.tl.pms.sys.service.ISysUserService;
import cm.tl.pms.sys.util.PageDtoUtil;
import cm.tl.pms.sys.util.PasswordStrengthValidator;
import cm.tl.pms.util.cache.CodeCache;
import cm.tl.pms.util.cache.LoginCache;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 系统-认证授权
 */
@RestController
@Slf4j
@RequestMapping("/sys/auth")
@Validated
public class LoginController {

    @Autowired
    ISysUserService sysUserService;
    @Autowired
    ISysUserRoleService sysUserRoleService;
    @Autowired
    ISysRoleService sysRoleService;


    /**
     * 登录-用户名密码
     * <p>
     * post http://127.0.0.1:8128/aIdataplatform/sys/auth/v1/login
     * {
     * "username":"guanliyuan",
     * "password":"123456"
     * }
     *
     * @param loginDto
     * @return
     */
    @PostMapping("/v1/login")
    public ResultMsg<SaTokenInfo> login(@Validated @RequestBody LoginDto loginDto) {
        log.info("输入的密码___{}", SecureUtil.sha256(loginDto.getPassword()));
        // 单机版：在map中创建了会话，token id等映射关系 // 写入cookie
        String username = loginDto.getUsername();
        SysUser sysUser = sysUserService.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getUserName, username));
        if (sysUser == null) {
            throw new CustomErrorException("用户名或密码不正确！");
        }
        if (sysUser.getEnable() == 0) {
            throw new CustomErrorException("用户已被禁用");
        }

        // 验证密码输入错误数
        SysUser passUser = sysUserService.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getUserName, username)
                .eq(SysUser::getPassword, SecureUtil.sha256(loginDto.getPassword())));
        if (ObjectUtil.isEmpty(passUser)) {
            numberOfValidationErrors(username, sysUser);
            throw new CustomErrorException("用户名或密码不正确！");
        }

        return getSaTokenInfoResultMsg(sysUser);
    }

    /**
     * 登录-手机号密码
     *
     * @param loginDto
     * @return
     */
    @PostMapping("/v1/loginPhone")
    public ResultMsg<SaTokenInfo> loginPhone(@Validated @RequestBody LoginPhoneDto loginDto) {
        log.info("输入的密码___{}", SecureUtil.sha256(loginDto.getPassword()));
        // 单机版：在map中创建了会话，token id等映射关系 // 写入cookie
        String phone = loginDto.getPhone();
        SysUser sysUser = sysUserService.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getPhone, phone));
        if (sysUser == null) {
            throw new CustomErrorException("手机号或密码不正确！");
        }
        if (sysUser.getEnable() == 0) {
            throw new CustomErrorException("用户已被禁用");
        }

        // 验证密码输入错误数
        SysUser passUser = sysUserService.getOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getPhone, phone)
                .eq(SysUser::getPassword, SecureUtil.sha256(loginDto.getPassword())));
        if (ObjectUtil.isEmpty(passUser)) {
            numberOfValidationErrors(phone, sysUser);
            throw new CustomErrorException("手机号或密码不正确！");
        }
        return getSaTokenInfoResultMsg(sysUser);
    }

    @NotNull
    private ResultMsg<SaTokenInfo> getSaTokenInfoResultMsg(SysUser sysUser) {
        SysUserRole role = sysUserRoleService.getOne(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, sysUser.getUserId()).last("limit 1"));
        sysUser.setRoleIds(role != null ? role.getRoleId().toString() : "0");
        StpUtil.login(sysUser.getUserId());
        // 获取用户的数据权限
        List<UserDataPower> userDataPowers = sysUserService.getUserDataPowers(sysUser.getUserId());
        UserInfoAndPowers.UserInfoAndPowersBuilder userInfoAndPowersBuilder = UserInfoAndPowers.builder()
                .deptId(sysUser.getDeptId())
                .userId(sysUser.getUserId())
                .userName(sysUser.getUserName())
                .roleId(role != null ? role.getRoleId() : 0)
                .nickName(sysUser.getNickName())
                .userDataPowers(userDataPowers);
        StpUtil.getSession().set(EasyAdminConstants.CURRENT_USER, userInfoAndPowersBuilder.build());
        return ResultMsg.successfulResult(StpUtil.getTokenInfo());
    }

    private void numberOfValidationErrors(String username, SysUser sysUser) {
        Integer numberOfErrors = LoginCache.get(username);
        if (ObjectUtil.isEmpty(numberOfErrors)) {
            numberOfErrors = 1;
        } else {
            numberOfErrors++;
        }
        if (numberOfErrors >= 5) {
            // 删除缓存内容，并修改用户状态为禁用
            LoginCache.remove(username);
            sysUser.setEnable(0);
            sysUserService.updateById(sysUser);
        } else {
            LoginCache.set(username, numberOfErrors);
        }
    }


    /**
     * 获取当前会话的token信息
     *
     * @return
     */
    @GetMapping("/v1/tokenInfo")
    @ApiOperationSupport(order = 2)
    public ResultMsg<SaTokenInfo> tokenInfo() {
        return ResultMsg.successfulResult(StpUtil.getTokenInfo());
    }


    /**
     * 获取当前用户信息
     *
     * @return
     */
    @GetMapping("/v1/userInfo")
    @ApiOperationSupport(order = 2)
    public ResultMsg<SysUser> userInfo() {
        if (!sysUserRoleService.verifyIsRole(StpUtil.getLoginIdAsLong())) {
            return ResultMsg.failResult(ErrorEnum.NO_PERMISSION);
        }
        return ResultMsg.data(sysUserService.getUserInfo());
    }

    /**
     * 获取在线用户信息
     *
     * @param page
     * @param limit
     * @return
     */
    @GetMapping("/v1/onlineUsers")
    @ApiOperationSupport(order = 2)
    public ResultMsg<Object> onlineUsers(@RequestParam(required = false, defaultValue = "1") int page,
                                         @RequestParam(required = false, defaultValue = "10") int limit) {
        EasySaTokenListener.ONLINE_USERS.sort((o1, o2) -> DateUtil.compare(o2.getLoginTime(), o1.getLoginTime()));
        PageDtoUtil pageDto = PageDtoUtil.getPageDto(EasySaTokenListener.ONLINE_USERS, page, limit);
        List<OnlineUser> pageList = (List<OnlineUser>) pageDto.getPageList();
        pageList.forEach(onlineUser -> {
            String keyLastActivityTime = StpUtil.getStpLogic().splicingKeyLastActiveTime(onlineUser.getTokenValue());
            String lastActivityTimeString = SaManager.getSaTokenDao().get(keyLastActivityTime);
            if (lastActivityTimeString != null) {
                long lastActivityTime = Long.parseLong(lastActivityTimeString);
                onlineUser.setLastActivityTime(DateUtil.date(lastActivityTime));
            }
        });
        Map<String, Object> map = new HashMap<>();
        map.put("list", pageList);
        map.put("total", pageDto.getTotal());
        return ResultMsg.successfulResult(map);
    }

    @GetMapping("/v1/kickOffline")
    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "踢人下线")
    @SaCheckPermission("online.user.kick")
    public ResultMsg<Void> kickOffline(String token) {
        log.info("踢人下线，token:{}", token);
        if (StpUtil.getTokenValue().equals(token)) {
            throw new CustomErrorException("不能踢自己啊老弟");
        }
        StpUtil.logoutByTokenValue(token);
        return ResultMsg.successfulResult();
    }

    @GetMapping("/v1/logout")
    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "登出")
    @SaCheckLogin
    public ResultMsg<Void> loginOut() {
        //获取token
        //   1. 尝试从request里读取 tokenName 默认值 satoken
        //   2. 尝试从请求体里面读取
        //   3. 尝试从header里读取
        //   4. 尝试从cookie里读取
        // 删除cookie
        StpUtil.logout();
        return ResultMsg.successfulResult();
    }

    /**
     * 手机号重置密码
     *
     * @param resetDto
     * @return
     */
    @PostMapping("/passwordResetPhone")
    public ResultMsg<String> passwordResetPhone(@Validated @RequestBody PasswordResetDto resetDto) {
        log.info("username = {}", resetDto.getUsername());
        PasswordRetrievalDto dto = resetDto.getRetrievalDto();

        // 验证用户是否存在
        if (!sysUserService.verifyUserName(resetDto.getUsername())) {
            throw new CustomErrorException("用户名不存在！");
        }
        // 验证手机号是否正确
        if (!sysUserService.verifyUserNameAndPhone(resetDto.getUsername(), dto.getPhone())) {
            return ResultMsg.failResult(ErrorEnum.ERROR_ENUM10025);
        }

        if (!CodeCache.containsCodeAndPhoneOrEmail(dto.getCodeId(), dto.getPhone(), dto.getCode())) {
            return ResultMsg.failResult(ErrorEnum.ERROR_ENUM10024);
        }
        // 验证密码强度
        if (!PasswordStrengthValidator.isValidPassword(resetDto.getNewPass())) {
            return ResultMsg.failResult(ErrorEnum.ERROR_ENUM10021);
        }
        // 验证两次密码是否一致
        if (!StrUtil.equals(resetDto.getNewPass(), resetDto.getConfirmPass())) {
            return ResultMsg.failResult(ErrorEnum.ERROR_ENUM10022);
        }
        // 更新密码
        return ResultMsg.result(sysUserService.passwordReset(resetDto.getUsername(), resetDto.getNewPass()));

    }

    /**
     * 邮箱重置密码
     *
     * @param resetDto
     * @return
     */
    @PostMapping("/passwordResetEmail")
    public ResultMsg<String> passwordResetEmail(@Validated @RequestBody PasswordResetEmailDto resetDto) {
        log.info("username = {}", resetDto.getUsername());
        PasswordRetrievalEmailDto dto = resetDto.getRetrievalDto();

        // 验证用户是否存在
        if (!sysUserService.verifyUserName(resetDto.getUsername())) {
            throw new CustomErrorException("用户名不存在！");
        }
        // 验证手机号是否正确
        if (!sysUserService.verifyUserNameAndEmail(resetDto.getUsername(), dto.getEmail())) {
            return ResultMsg.failResult(ErrorEnum.ERROR_ENUM10025);
        }

        if (!CodeCache.containsCodeAndPhoneOrEmail(dto.getCodeId(), dto.getEmail(), dto.getCode())) {
            return ResultMsg.failResult(ErrorEnum.ERROR_ENUM10024);
        }
        // 验证密码强度
        if (!PasswordStrengthValidator.isValidPassword(resetDto.getNewPass())) {
            return ResultMsg.failResult(ErrorEnum.ERROR_ENUM10021);
        }
        // 验证两次密码是否一致
        if (!StrUtil.equals(resetDto.getNewPass(), resetDto.getConfirmPass())) {
            return ResultMsg.failResult(ErrorEnum.ERROR_ENUM10022);
        }
        // 更新密码
        return ResultMsg.result(sysUserService.passwordReset(resetDto.getUsername(), resetDto.getNewPass()));
    }

}
